/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.api.db.hibernate;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.hibernate.transform.DistinctRootEntityResultTransformer;
import org.hibernate.transform.Transformers;
import org.openmrs.Concept;
import org.openmrs.ConceptAnswer;
import org.openmrs.ConceptClass;
import org.openmrs.ConceptComplex;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptDescription;
import org.openmrs.ConceptMap;
import org.openmrs.ConceptName;
import org.openmrs.ConceptNameTag;
import org.openmrs.ConceptNumeric;
import org.openmrs.ConceptProposal;
import org.openmrs.ConceptSearchResult;
import org.openmrs.ConceptSet;
import org.openmrs.ConceptSetDerived;
import org.openmrs.ConceptSource;
import org.openmrs.ConceptStopWord;
import org.openmrs.ConceptWord;
import org.openmrs.Drug;
import org.openmrs.DrugIngredient;
import org.openmrs.api.ConceptNameType;
import org.openmrs.api.ConceptService;
import org.openmrs.api.context.Context;
import org.openmrs.api.db.ConceptDAO;
import org.openmrs.api.db.DAOException;
import org.openmrs.util.OpenmrsConstants;
/**
* The Hibernate class for Concepts, Drugs, and related classes. <br/>
* <br/>
* Use the {@link ConceptService} to access these methods
*
* @see ConceptService
*/
public class HibernateConceptDAO implements ConceptDAO {
protected final Log log = LogFactory.getLog(getClass());
private SessionFactory sessionFactory;
/**
* Sets the session factory
*
* @param sessionFactory
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptComplex(java.lang.Integer)
*/
public ConceptComplex getConceptComplex(Integer conceptId) {
ConceptComplex cc;
Object obj = sessionFactory.getCurrentSession().get(ConceptComplex.class, conceptId);
// If Concept has already been read & cached, we may get back a Concept instead of
// ConceptComplex. If this happens, we need to clear the object from the cache
// and re-fetch it as a ConceptComplex
if (obj != null && !obj.getClass().equals(ConceptComplex.class)) {
sessionFactory.getCurrentSession().evict(obj); // remove from cache
// session.get() did not work here, we need to perform a query to get a ConceptComplex
Query query = sessionFactory.getCurrentSession().createQuery("from ConceptComplex where conceptId = :conceptId")
.setParameter("conceptId", conceptId);
obj = query.uniqueResult();
}
cc = (ConceptComplex) obj;
return cc;
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConcept(org.openmrs.Concept)
*/
public Concept saveConcept(Concept concept) throws DAOException {
if ((concept.getConceptId() != null) && (concept.getConceptId() > 0)) {
// this method checks the concept_numeric, concept_derived, etc tables
// to see if a row exists there or not. This is needed because hibernate
// doesn't like to insert into concept_numeric but update concept in the
// same go. It assumes that its either in both tables or no tables
insertRowIntoSubclassIfNecessary(concept);
}
sessionFactory.getCurrentSession().saveOrUpdate(concept);
return concept;
}
/**
* Convenience method that will check this concept for subtype values (ConceptNumeric,
* ConceptDerived, etc) and insert a line into that subtable if needed. This prevents a
* hibernate ConstraintViolationException
*
* @param concept the concept that will be inserted
*/
private void insertRowIntoSubclassIfNecessary(Concept concept) {
Connection connection = sessionFactory.getCurrentSession().connection();
PreparedStatement ps = null;
PreparedStatement ps2 = null;
// check the concept_numeric table
if (concept instanceof ConceptNumeric) {
try {
ps = connection
.prepareStatement("SELECT * FROM concept WHERE concept_id = ? and not exists (select * from concept_numeric WHERE concept_id = ?)");
ps.setInt(1, concept.getConceptId());
ps.setInt(2, concept.getConceptId());
ps.execute();
// Converting to concept numeric: A single concept row exists, but concept numeric has not been populated yet.
if (ps.getResultSet().next()) {
// we have to evict the current concept out of the session because
// the user probably had to change the class of this object to get it
// to now be a numeric
// (must be done before the "insert into...")
sessionFactory.getCurrentSession().clear();
ps2 = connection.prepareStatement("INSERT INTO concept_numeric (concept_id, precise) VALUES (?, false)");
ps2.setInt(1, concept.getConceptId());
ps2.executeUpdate();
}
// Converting from concept numeric: The concept and concept numeric rows both exist, so we need to delete concept_numeric.
else {
//concept is changed from numeric to something else
// hence row should be deleted from the concept_numeric
if (!concept.isNumeric()) {
ps2 = connection.prepareStatement("DELETE FROM concept_numeric WHERE concept_id = ?");
ps2.setInt(1, concept.getConceptId());
ps2.executeUpdate();
} else {
// it is indeed numeric now... don't delete
}
}
}
catch (SQLException e) {
log.error("Error while trying to see if this ConceptNumeric is in the concept_numeric table already", e);
}
finally {
if (ps != null) {
try {
ps.close();
}
catch (SQLException e) {
log.error("Error generated while closing statement", e);
}
}
if (ps2 != null) {
try {
ps2.close();
}
catch (SQLException e) {
log.error("Error generated while closing statement", e);
}
}
}
}
// check the concept complex table
else if (concept instanceof ConceptComplex) {
try {
ps = connection
.prepareStatement("SELECT * FROM concept WHERE concept_id = ? and not exists (select * from concept_complex WHERE concept_id = ?)");
ps.setInt(1, concept.getConceptId());
ps.setInt(2, concept.getConceptId());
ps.execute();
// Converting to concept complex: A single concept row exists, but concept complex has not been populated yet.
if (ps.getResultSet().next()) {
// we have to evict the current concept out of the session because
// the user probably had to change the class of this object to get it
// to now be a ConceptComplex
// (must be done before the "insert into...")
sessionFactory.getCurrentSession().clear();
// Add an empty row into the concept_complex table
ps2 = connection.prepareStatement("INSERT INTO concept_complex (concept_id) VALUES (?)");
ps2.setInt(1, concept.getConceptId());
ps2.executeUpdate();
}
// Converting from concept complex: The concept and concept complex rows both exist, so we need to delete the concept_complex row.
// no stub insert is needed because either a concept row doesn't exist OR a concept_complex row does exist
else {
// concept is changed from complex to something else
// hence row should be deleted from the concept_complex
if (!concept.isComplex()) {
ps2 = connection.prepareStatement("DELETE FROM concept_complex WHERE concept_id = ?");
ps2.setInt(1, concept.getConceptId());
ps2.executeUpdate();
} else {
// it is indeed numeric now... don't delete
}
}
}
catch (SQLException e) {
log.error("Error while trying to see if this ConceptComplex is in the concept_complex table already", e);
}
finally {
if (ps != null) {
try {
ps.close();
}
catch (SQLException e) {
log.error("Error generated while closing statement", e);
}
}
if (ps2 != null) {
try {
ps2.close();
}
catch (SQLException e) {
log.error("Error generated while closing statement", e);
}
}
}
}
}
/**
* @see org.openmrs.api.db.ConceptDAO#purgeConcept(org.openmrs.Concept)
*/
public void purgeConcept(Concept concept) throws DAOException {
// must delete all the stored concept words first
sessionFactory.getCurrentSession().createQuery("delete from ConceptWord where concept_id = :c").setInteger("c",
concept.getConceptId()).executeUpdate();
// now we can safely delete the concept
sessionFactory.getCurrentSession().delete(concept);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConcept(java.lang.Integer)
*/
public Concept getConcept(Integer conceptId) throws DAOException {
return (Concept) sessionFactory.getCurrentSession().get(Concept.class, conceptId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptName(java.lang.Integer)
*/
public ConceptName getConceptName(Integer conceptNameId) throws DAOException {
return (ConceptName) sessionFactory.getCurrentSession().get(ConceptName.class, conceptNameId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptAnswer(java.lang.Integer)
*/
public ConceptAnswer getConceptAnswer(Integer conceptAnswerId) throws DAOException {
return (ConceptAnswer) sessionFactory.getCurrentSession().get(ConceptAnswer.class, conceptAnswerId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConcepts(java.lang.String, boolean, boolean)
*/
@SuppressWarnings("unchecked")
public List<Concept> getAllConcepts(String sortBy, boolean asc, boolean includeRetired) throws DAOException {
String sql = "from Concept concept";
if (!includeRetired)
sql += " where retired = false ";
try {
Concept.class.getDeclaredField(sortBy);
}
catch (NoSuchFieldException e) {
try {
ConceptName.class.getDeclaredField(sortBy);
sortBy = "names." + sortBy;
}
catch (NoSuchFieldException e2) {
sortBy = "conceptId";
}
}
sql += " order by concept." + sortBy;
if (!asc)
sql += " desc";
else
sql += " asc";
Query query = sessionFactory.getCurrentSession().createQuery(sql);
return (List<Concept>) query.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveDrug(org.openmrs.Drug)
*/
public Drug saveDrug(Drug drug) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(drug);
return drug;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getDrug(java.lang.Integer)
*/
public Drug getDrug(Integer drugId) throws DAOException {
return (Drug) sessionFactory.getCurrentSession().get(Drug.class, drugId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String, org.openmrs.Concept, boolean)
*/
@SuppressWarnings("unchecked")
public List<Drug> getDrugs(String drugName, Concept concept, boolean includeRetired) throws DAOException {
Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
if (includeRetired == false)
searchCriteria.add(Restrictions.eq("drug.retired", false));
if (concept != null)
searchCriteria.add(Restrictions.eq("drug.concept", concept));
if (drugName != null)
searchCriteria.add(Restrictions.eq("drug.name", drugName));
return (List<Drug>) searchCriteria.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getDrugs(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<Drug> getDrugs(String phrase) throws DAOException {
List<String> words = ConceptWord.getUniqueWords(phrase);
List<Drug> conceptDrugs = new Vector<Drug>();
if (words.size() > 0) {
Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
searchCriteria.add(Restrictions.eq("drug.retired", false));
Iterator<String> word = words.iterator();
searchCriteria.add(Restrictions.like("name", word.next(), MatchMode.ANYWHERE));
while (word.hasNext()) {
String w = word.next();
log.debug(w);
searchCriteria.add(Restrictions.like("name", w, MatchMode.ANYWHERE));
}
searchCriteria.addOrder(Order.asc("drug.concept"));
conceptDrugs = searchCriteria.list();
}
return conceptDrugs;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptClass(java.lang.Integer)
*/
public ConceptClass getConceptClass(Integer i) throws DAOException {
return (ConceptClass) sessionFactory.getCurrentSession().get(ConceptClass.class, i);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptClasses(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<ConceptClass> getConceptClasses(String name) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class);
if (name != null)
crit.add(Restrictions.eq("name", name));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptClasses(boolean)
*/
@SuppressWarnings("unchecked")
public List<ConceptClass> getAllConceptClasses(boolean includeRetired) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptClass.class);
// Minor bug - was assigning includeRetired instead of evaluating
if (includeRetired == false)
crit.add(Restrictions.eq("retired", false));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptClass(org.openmrs.ConceptClass)
*/
public ConceptClass saveConceptClass(ConceptClass cc) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(cc);
return cc;
}
/**
* @see org.openmrs.api.db.ConceptDAO#purgeConceptClass(org.openmrs.ConceptClass)
*/
public void purgeConceptClass(ConceptClass cc) throws DAOException {
sessionFactory.getCurrentSession().delete(cc);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptDatatype(java.lang.Integer)
*/
public ConceptDatatype getConceptDatatype(Integer i) {
return (ConceptDatatype) sessionFactory.getCurrentSession().get(ConceptDatatype.class, i);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptDatatypes(boolean)
*/
@SuppressWarnings("unchecked")
public List<ConceptDatatype> getAllConceptDatatypes(boolean includeRetired) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class);
if (includeRetired == false)
crit.add(Restrictions.eq("retired", false));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptDatatypes(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<ConceptDatatype> getConceptDatatypes(String name) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class);
if (name != null)
crit.add(Restrictions.like("name", name, MatchMode.START));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByName(String)
*/
public ConceptDatatype getConceptDatatypeByName(String name) throws DAOException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptDatatype.class);
if (name != null) {
criteria.add(Restrictions.eq("name", name));
}
return (ConceptDatatype) criteria.uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptDatatype(org.openmrs.ConceptDatatype)
*/
public ConceptDatatype saveConceptDatatype(ConceptDatatype cd) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(cd);
return cd;
}
/**
* @see org.openmrs.api.db.ConceptDAO#purgeConceptDatatype(org.openmrs.ConceptDatatype)
*/
public void purgeConceptDatatype(ConceptDatatype cd) throws DAOException {
sessionFactory.getCurrentSession().delete(cd);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptNumeric(java.lang.Integer)
*/
public ConceptNumeric getConceptNumeric(Integer i) {
ConceptNumeric cn;
Object obj = sessionFactory.getCurrentSession().get(ConceptNumeric.class, i);
// If Concept has already been read & cached, we may get back a Concept instead of
// ConceptNumeric. If this happens, we need to clear the object from the cache
// and re-fetch it as a ConceptNumeric
if (obj != null && !obj.getClass().equals(ConceptNumeric.class)) {
sessionFactory.getCurrentSession().evict(obj); // remove from cache
// session.get() did not work here, we need to perform a query to get a ConceptNumeric
Query query = sessionFactory.getCurrentSession().createQuery("from ConceptNumeric where conceptId = :conceptId")
.setParameter("conceptId", i);
obj = query.uniqueResult();
}
cn = (ConceptNumeric) obj;
return cn;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConcepts(java.lang.String, java.util.Locale, boolean,
* java.util.List, java.util.List)
*/
@SuppressWarnings("unchecked")
public List<Concept> getConcepts(String name, Locale loc, boolean searchOnPhrase, List<ConceptClass> classes,
List<ConceptDatatype> datatypes) throws DAOException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Concept.class);
criteria.add(Restrictions.eq("retired", false));
if (name != null) {
if (loc == null)
throw new DAOException("Locale must be not null");
criteria.createAlias("names", "names");
MatchMode matchmode = MatchMode.EXACT;
if (searchOnPhrase)
matchmode = MatchMode.ANYWHERE;
criteria.add(Restrictions.ilike("names.name", name, matchmode));
String language = loc.getLanguage();
if (language.length() > 2) {
// if searching in specific locale like en_US
criteria.add(Restrictions.or(Restrictions.eq("names.locale", loc), Restrictions.eq("names.locale",
new Locale(loc.getLanguage().substring(0, 2)))));
} else {
// if searching in general locale like just "en"
// criteria.add(Restrictions.like("names.locale", loc.getLanguage(), MatchMode.START));
}
}
if (classes.size() > 0)
criteria.add(Restrictions.in("conceptClass", classes));
if (datatypes.size() > 0)
criteria.add(Restrictions.in("datatype", datatypes));
return criteria.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptWords(java.lang.String, java.util.List, boolean,
* java.util.List, java.util.List, java.util.List, java.util.List, org.openmrs.Concept,
* java.lang.Integer, java.lang.Integer)
*/
@SuppressWarnings("unchecked")
public List<ConceptWord> getConceptWords(String phrase, List<Locale> locales, boolean includeRetired,
List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
List<ConceptDatatype> excludeDatatypes, Concept answersToConcept, Integer start, Integer size)
throws DAOException {
Criteria searchCriteria = createConceptWordSearchCriteria(phrase, locales, includeRetired, requireClasses,
excludeClasses, requireDatatypes, excludeDatatypes, answersToConcept);
searchCriteria.addOrder(Order.desc("cw1.weight"));
List<ConceptWord> conceptWords = new Vector<ConceptWord>();
if (searchCriteria != null) {
if (start != null)
searchCriteria.setFirstResult(start);
if (size != null && size > 0)
searchCriteria.setMaxResults(size);
return searchCriteria.list();
}
if (log.isDebugEnabled())
log.debug("No matching ConceptWords found");
return conceptWords;
}
/**
* gets questions for the given answer concept
*
* @see org.openmrs.api.db.ConceptDAO#getConceptsByAnswer(org.openmrs.Concept)
*/
@SuppressWarnings("unchecked")
public List<Concept> getConceptsByAnswer(Concept concept) {
// TODO broken until Hibernate fixes component and HQL code
String q = "select c from Concept c join c.answers ca where ca.answerConcept = :answer";
Query query = sessionFactory.getCurrentSession().createQuery(q);
query.setParameter("answer", concept);
return query.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getPrevConcept(org.openmrs.Concept)
*/
@SuppressWarnings("unchecked")
public Concept getPrevConcept(Concept c) {
Integer i = c.getConceptId();
List<Concept> concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class).add(
Restrictions.lt("conceptId", i)).addOrder(Order.desc("conceptId")).setFetchSize(1).list();
if (concepts.size() < 1)
return null;
return concepts.get(0);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getNextConcept(org.openmrs.Concept)
*/
@SuppressWarnings("unchecked")
public Concept getNextConcept(Concept c) {
Integer i = c.getConceptId();
List<Concept> concepts = sessionFactory.getCurrentSession().createCriteria(Concept.class).add(
Restrictions.gt("conceptId", i)).addOrder(Order.asc("conceptId")).setFetchSize(1).list();
if (concepts.size() < 1)
return null;
return concepts.get(0);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptsWithDrugsInFormulary()
*/
@SuppressWarnings("unchecked")
public List<Concept> getConceptsWithDrugsInFormulary() {
Query query = sessionFactory.getCurrentSession().createQuery(
"select distinct concept from Drug d where d.retired = false");
return query.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#purgeDrug(org.openmrs.Drug)
*/
public void purgeDrug(Drug drug) throws DAOException {
sessionFactory.getCurrentSession().delete(drug);
}
/**
* @see org.openmrs.api.db.ConceptDAO#updateConceptWord(org.openmrs.Concept)
*/
public void updateConceptWord(Concept concept) throws DAOException {
log.debug("updateConceptWord(" + concept + ")");
if (concept != null) {
// remove all old words
if (concept.getConceptId() != null && concept.getConceptId() > 0)
deleteConceptWord(concept);
// add all new words
Collection<ConceptWord> words = ConceptWord.makeConceptWords(concept);
log.debug("words: " + words);
for (ConceptWord word : words) {
word.setWeight(weighConceptWord(word));
try {
sessionFactory.getCurrentSession().save(word);
}
catch (NonUniqueObjectException e) {
ConceptWord tmp = (ConceptWord) sessionFactory.getCurrentSession().merge(word);
sessionFactory.getCurrentSession().evict(tmp);
sessionFactory.getCurrentSession().save(word);
}
}
}
}
/**
* Deletes all concept words for a concept. Called by {@link #updateConceptWord(Concept)}
*
* @param concept
* @throws DAOException
*/
@SuppressWarnings("unchecked")
private void deleteConceptWord(Concept concept) throws DAOException {
log.debug("deletConceptWord(" + concept + ")");
if (concept != null) {
if (log.isDebugEnabled()) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptWord.class);
crit.add(Restrictions.eq("concept", concept));
List<ConceptWord> words = crit.list();
Integer authUserId = null;
if (Context.isAuthenticated())
authUserId = Context.getAuthenticatedUser().getUserId();
log.debug(authUserId + "|ConceptWord|" + words);
}
sessionFactory.getCurrentSession().createQuery("delete from ConceptWord where concept_id = :c").setInteger("c",
concept.getConceptId()).executeUpdate();
}
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptProposal(org.openmrs.ConceptProposal)
*/
public ConceptProposal saveConceptProposal(ConceptProposal cp) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(cp);
return cp;
}
/**
* @see org.openmrs.api.db.ConceptDAO#purgeConceptProposal(org.openmrs.ConceptProposal)
*/
public void purgeConceptProposal(ConceptProposal cp) throws DAOException {
sessionFactory.getCurrentSession().delete(cp);
return;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptProposals(boolean)
*/
@SuppressWarnings("unchecked")
public List<ConceptProposal> getAllConceptProposals(boolean includeCompleted) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
if (includeCompleted == false) {
crit.add(Restrictions.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
}
crit.addOrder(Order.asc("originalText"));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptProposal(java.lang.Integer)
*/
public ConceptProposal getConceptProposal(Integer conceptProposalId) throws DAOException {
return (ConceptProposal) sessionFactory.getCurrentSession().get(ConceptProposal.class, conceptProposalId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptProposals(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<ConceptProposal> getConceptProposals(String text) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
crit.add(Restrictions.eq("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
crit.add(Restrictions.eq("originalText", text));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getProposedConcepts(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<Concept> getProposedConcepts(String text) throws DAOException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptProposal.class);
crit.add(Restrictions.ne("state", OpenmrsConstants.CONCEPT_PROPOSAL_UNMAPPED));
crit.add(Restrictions.eq("originalText", text));
crit.add(Restrictions.isNotNull("mappedConcept"));
crit.setProjection(Projections.distinct(Projections.property("mappedConcept")));
return crit.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptSetsByConcept(org.openmrs.Concept)
*/
@SuppressWarnings("unchecked")
public List<ConceptSet> getConceptSetsByConcept(Concept concept) {
return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class).add(
Restrictions.eq("conceptSet", concept)).addOrder(Order.asc("sortWeight")).list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getSetsContainingConcept(org.openmrs.Concept)
*/
@SuppressWarnings("unchecked")
public List<ConceptSet> getSetsContainingConcept(Concept concept) {
return sessionFactory.getCurrentSession().createCriteria(ConceptSet.class).add(Restrictions.eq("concept", concept))
.list();
}
//TODO: eventually, this method should probably just run updateConceptSetDerived(Concept) inside an iteration of all concepts... (or something else less transactionally-intense)
/**
* @see org.openmrs.api.db.ConceptDAO#updateConceptSetDerived()
*/
public void updateConceptSetDerived() throws DAOException {
sessionFactory.getCurrentSession().createQuery("delete from ConceptSetDerived").executeUpdate();
try {
// remake the derived table by copying over the basic concept_set table
sessionFactory
.getCurrentSession()
.connection()
.prepareStatement(
"insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs.concept_id, cs.concept_set, cs.sort_weight from concept_set cs where not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs.concept_id and csd.concept_set = cs.concept_set)")
.execute();
// burst the concept sets -- make grandchildren direct children of grandparents
sessionFactory
.getCurrentSession()
.connection()
.prepareStatement(
"insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs1.concept_id, cs2.concept_set, cs1.sort_weight from concept_set cs1 join concept_set cs2 where cs2.concept_id = cs1.concept_set and not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs1.concept_id and csd.concept_set = cs2.concept_set)")
.execute();
// burst the concept sets -- make greatgrandchildren direct child of greatgrandparents
sessionFactory
.getCurrentSession()
.connection()
.prepareStatement(
"insert into concept_set_derived (concept_id, concept_set, sort_weight) select cs1.concept_id, cs3.concept_set, cs1.sort_weight from concept_set cs1 join concept_set cs2 join concept_set cs3 where cs1.concept_set = cs2.concept_id and cs2.concept_set = cs3.concept_id and not exists (select concept_id from concept_set_derived csd where csd.concept_id = cs1.concept_id and csd.concept_set = cs3.concept_set)")
.execute();
// TODO This 'algorithm' only solves three layers of children. Options for correction:
// 1) Add a few more join statements to cover 5 layers (conceivable upper limit of layers)
// 2) Find the deepest layer and programmatically create the sql statements
// 3) Run the joins on
}
catch (SQLException e) {
throw new DAOException(e);
}
}
/**
* utility method used in updateConceptSetDerived(...)
*
* @param List of parent Concept objects
* @param Concept current
* @return Set of ConceptSetDerived
*/
private Set<ConceptSetDerived> deriveChildren(List<Concept> parents, Concept current) {
Set<ConceptSetDerived> updates = new HashSet<ConceptSetDerived>();
ConceptSetDerived derivedSet = null;
// make each child a direct child of each parent/grandparent
for (ConceptSet childSet : current.getConceptSets()) {
Concept child = childSet.getConcept();
log.debug("Deriving child: " + child.getConceptId());
Double sort_weight = childSet.getSortWeight();
for (Concept parent : parents) {
log.debug("Matching child: " + child.getConceptId() + " with parent: " + parent.getConceptId());
derivedSet = new ConceptSetDerived(parent, child, sort_weight++);
updates.add(derivedSet);
}
//recurse if this child is a set as well
if (child.isSet()) {
log.debug("Concept id: " + child.getConceptId() + " is a set");
List<Concept> new_parents = new Vector<Concept>();
new_parents.addAll(parents);
new_parents.add(child);
updates.addAll(deriveChildren(new_parents, child));
}
}
return updates;
}
/**
* @see org.openmrs.api.db.ConceptDAO#updateConceptSetDerived(org.openmrs.Concept)
*/
public void updateConceptSetDerived(Concept concept) throws DAOException {
log.debug("Updating concept set derivisions for #" + concept.getConceptId().toString());
// deletes current concept's sets and matching parent's sets
//recursively get all parents
List<Concept> parents = getParents(concept);
// delete this concept's children and their bursted parents
for (Concept parent : parents) {
sessionFactory
.getCurrentSession()
.createQuery(
"delete from ConceptSetDerived csd where csd.concept in (select cs.concept from ConceptSet cs where cs.conceptSet = :c) and csd.conceptSet = :parent)")
.setParameter("c", concept).setParameter("parent", parent).executeUpdate();
}
//set of updates to be passed to the server (unique list)
Set<ConceptSetDerived> updates = new HashSet<ConceptSetDerived>();
//add parents as sets of parents below
ConceptSetDerived csd;
for (Integer a = 0; a < parents.size() - 1; a++) {
Concept set = parents.get(a);
for (Integer b = a + 1; b < parents.size(); b++) {
Concept conc = parents.get(b);
csd = new ConceptSetDerived(set, conc, Double.valueOf(b.doubleValue()));
updates.add(csd);
}
}
//recursively add parents to children
updates.addAll(deriveChildren(parents, concept));
for (ConceptSetDerived c : updates) {
sessionFactory.getCurrentSession().saveOrUpdate(c);
}
}
/**
* returns a list of n-generations of parents of a concept in a concept set
*
* @param Concept current
* @return List<Concept>
* @throws DAOException
*/
@SuppressWarnings("unchecked")
private List<Concept> getParents(Concept current) throws DAOException {
List<Concept> parents = new Vector<Concept>();
if (current != null) {
Query query = sessionFactory.getCurrentSession().createQuery(
"from Concept c join c.conceptSets sets where sets.concept = ?").setEntity(0, current);
List<Concept> immed_parents = query.list();
for (Concept c : immed_parents) {
parents.addAll(getParents(c));
}
parents.add(current);
if (log.isDebugEnabled()) {
log.debug("parents found: ");
for (Concept c : parents) {
log.debug("id: " + c.getConceptId());
}
}
}
return parents;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getLocalesOfConceptNames()
*/
public Set<Locale> getLocalesOfConceptNames() {
Set<Locale> locales = new HashSet<Locale>();
Query query = sessionFactory.getCurrentSession().createQuery("select distinct locale from ConceptName");
for (Object locale : query.list()) {
locales.add((Locale) locale);
}
return locales;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptNameTag(java.lang.Integer)
*/
public ConceptNameTag getConceptNameTag(Integer i) {
return (ConceptNameTag) sessionFactory.getCurrentSession().get(ConceptNameTag.class, i);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByName(java.lang.String)
*/
public ConceptNameTag getConceptNameTagByName(String name) {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(ConceptNameTag.class).add(
Restrictions.eq("tag", name));
if (crit.list().size() < 1) {
log.warn("No concept name tag found with name: " + name);
return null;
}
return (ConceptNameTag) crit.list().get(0);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptNameTags()
*/
@SuppressWarnings("unchecked")
public List<ConceptNameTag> getAllConceptNameTags() {
return sessionFactory.getCurrentSession().createQuery("from ConceptNameTag cnt order by cnt.tag").list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptSource(java.lang.Integer)
*/
public ConceptSource getConceptSource(Integer conceptSourceId) {
return (ConceptSource) sessionFactory.getCurrentSession().get(ConceptSource.class, conceptSourceId);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptSources()
*/
@SuppressWarnings("unchecked")
public List<ConceptSource> getAllConceptSources() {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class);
criteria.add(Restrictions.eq("retired", false));
return criteria.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#deleteConceptSource(org.openmrs.ConceptSource)
*/
public ConceptSource deleteConceptSource(ConceptSource cs) throws DAOException {
sessionFactory.getCurrentSession().delete(cs);
return cs;
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptSource(org.openmrs.ConceptSource)
*/
public ConceptSource saveConceptSource(ConceptSource conceptSource) throws DAOException {
sessionFactory.getCurrentSession().saveOrUpdate(conceptSource);
return conceptSource;
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptNameTag(org.openmrs.ConceptNameTag)
*/
public ConceptNameTag saveConceptNameTag(ConceptNameTag nameTag) {
if (nameTag == null)
return null;
sessionFactory.getCurrentSession().saveOrUpdate(nameTag);
return nameTag;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getMaxConceptId()
*/
public Integer getMinConceptId() {
Query query = sessionFactory.getCurrentSession().createQuery("select min(conceptId) from Concept");
return (Integer) query.uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getMaxConceptId()
*/
public Integer getMaxConceptId() {
Query query = sessionFactory.getCurrentSession().createQuery("select max(conceptId) from Concept");
return (Integer) query.uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#conceptIterator()
*/
public Iterator<Concept> conceptIterator() {
return new ConceptIterator();
}
/**
* An iterator that loops over all concepts in the dictionary one at a time
*/
private class ConceptIterator implements Iterator<Concept> {
Concept currentConcept = null;
Concept nextConcept;
public ConceptIterator() {
final int firstConceptId = getMinConceptId();
nextConcept = getConcept(firstConceptId);
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return (nextConcept != null);
}
/**
* @see java.util.Iterator#next()
*/
public Concept next() {
if (currentConcept != null) {
sessionFactory.getCurrentSession().evict(currentConcept);
}
currentConcept = nextConcept;
nextConcept = getNextConcept(currentConcept);
return currentConcept;
}
/**
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptsByMapping(java.lang.String, java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<Concept> getConceptsByMapping(String code, String sourceName, boolean includeRetired) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class);
// make this criteria return a list of concepts
criteria.setProjection(Projections.property("concept"));
// match the source code to the passed code
criteria.add(Restrictions.eq("sourceCode", code));
// join to conceptSource and match to the h17Code or source name
criteria.createAlias("source", "conceptSource");
criteria.add(Restrictions.or(Restrictions.eq("conceptSource.name", sourceName), Restrictions.eq(
"conceptSource.hl7Code", sourceName)));
criteria.createAlias("concept", "concept");
if (!includeRetired) {
// ignore retired concepts
criteria.add(Restrictions.eq("concept.retired", false));
} else {
// sort retired concepts to the end of the list
criteria.addOrder(Order.asc("concept.retired"));
}
// we only want distinct concepts
criteria.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
return (List<Concept>) criteria.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptByUuid(java.lang.String)
*/
public Concept getConceptByUuid(String uuid) {
return (Concept) sessionFactory.getCurrentSession().createQuery("from Concept c where c.uuid = :uuid").setString(
"uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptClassByUuid(java.lang.String)
*/
public ConceptClass getConceptClassByUuid(String uuid) {
return (ConceptClass) sessionFactory.getCurrentSession().createQuery("from ConceptClass cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
public ConceptAnswer getConceptAnswerByUuid(String uuid) {
return (ConceptAnswer) sessionFactory.getCurrentSession().createQuery("from ConceptAnswer cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
public ConceptName getConceptNameByUuid(String uuid) {
return (ConceptName) sessionFactory.getCurrentSession().createQuery("from ConceptName cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
public ConceptSet getConceptSetByUuid(String uuid) {
return (ConceptSet) sessionFactory.getCurrentSession().createQuery("from ConceptSet cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
public ConceptSetDerived getConceptSetDerivedByUuid(String uuid) {
return (ConceptSetDerived) sessionFactory.getCurrentSession().createQuery(
"from ConceptSetDerived cc where cc.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
public ConceptSource getConceptSourceByUuid(String uuid) {
return (ConceptSource) sessionFactory.getCurrentSession().createQuery("from ConceptSource cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
public ConceptWord getConceptWordByUuid(String uuid) {
return (ConceptWord) sessionFactory.getCurrentSession().createQuery("from ConceptWord cc where cc.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptDatatypeByUuid(java.lang.String)
*/
public ConceptDatatype getConceptDatatypeByUuid(String uuid) {
return (ConceptDatatype) sessionFactory.getCurrentSession().createQuery(
"from ConceptDatatype cd where cd.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptNumericByUuid(java.lang.String)
*/
public ConceptNumeric getConceptNumericByUuid(String uuid) {
return (ConceptNumeric) sessionFactory.getCurrentSession().createQuery(
"from ConceptNumeric cn where cn.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptProposalByUuid(java.lang.String)
*/
public ConceptProposal getConceptProposalByUuid(String uuid) {
return (ConceptProposal) sessionFactory.getCurrentSession().createQuery(
"from ConceptProposal cp where cp.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getDrugByUuid(java.lang.String)
*/
public Drug getDrugByUuid(String uuid) {
return (Drug) sessionFactory.getCurrentSession().createQuery("from Drug d where d.uuid = :uuid").setString("uuid",
uuid).uniqueResult();
}
public DrugIngredient getDrugIngredientByUuid(String uuid) {
return (DrugIngredient) sessionFactory.getCurrentSession().createQuery("from DrugIngredient d where d.uuid = :uuid")
.setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptUuids()
*/
public Map<Integer, String> getConceptUuids() {
Map<Integer, String> ret = new HashMap<Integer, String>();
Query q = sessionFactory.getCurrentSession().createQuery("select conceptId, uuid from Concept");
List<Object[]> list = q.list();
for (Object[] o : list)
ret.put((Integer) o[0], (String) o[1]);
return ret;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptDescriptionByUuid(java.lang.String)
*/
public ConceptDescription getConceptDescriptionByUuid(String uuid) {
return (ConceptDescription) sessionFactory.getCurrentSession().createQuery(
"from ConceptDescription cd where cd.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptNameTagByUuid(java.lang.String)
*/
public ConceptNameTag getConceptNameTagByUuid(String uuid) {
return (ConceptNameTag) sessionFactory.getCurrentSession().createQuery(
"from ConceptNameTag cnt where cnt.uuid = :uuid").setString("uuid", uuid).uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptsByConceptSourceName(java.lang.String,
* java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<ConceptMap> getConceptsByConceptSource(ConceptSource conceptSource) throws DAOException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptMap.class, "map");
criteria.add(Restrictions.eq("map.source", conceptSource));
return (List<ConceptMap>) criteria.list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptSourceByName(java.lang.String)
*/
public ConceptSource getConceptSourceByName(String conceptSourceName) throws DAOException {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptSource.class, "source");
criteria.add(Restrictions.eq("source.name", conceptSourceName));
return (ConceptSource) criteria.uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getSavedConceptDatatype(org.openmrs.Concept)
*/
public ConceptDatatype getSavedConceptDatatype(Concept concept) {
SQLQuery sql = sessionFactory.getCurrentSession().createSQLQuery(
"select datatype.* from " + "concept_datatype datatype, " + "concept concept " + "where "
+ "datatype.concept_datatype_id = concept.datatype_id " + "and concept.concept_id=:conceptId")
.addEntity(ConceptDatatype.class);
sql.setInteger("conceptId", concept.getConceptId());
return (ConceptDatatype) sql.uniqueResult();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getSavedConceptName(org.openmrs.ConceptName)
*/
@Override
public ConceptName getSavedConceptName(ConceptName conceptName) {
sessionFactory.getCurrentSession().refresh(conceptName);
return conceptName;
}
/**
* @see org.openmrs.api.db.ConceptDAO#getConceptStopWords(java.util.Locale)
*/
public List<String> getConceptStopWords(Locale locale) throws DAOException {
locale = (locale == null ? Context.getLocale() : locale);
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class);
criteria.add(Restrictions.eq("locale", locale));
List<ConceptStopWord> stopWordList = criteria.list();
List<String> stopWords = new ArrayList<String>();
for (ConceptStopWord conceptStopWord : stopWordList) {
stopWords.add(conceptStopWord.getValue());
}
return stopWords;
}
/**
* @see org.openmrs.api.db.ConceptDAO#saveConceptStopWord(org.openmrs.ConceptStopWord)
*/
public ConceptStopWord saveConceptStopWord(ConceptStopWord conceptStopWord) throws DAOException {
if (conceptStopWord != null) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class);
criteria.add(Restrictions.eq("value", conceptStopWord.getValue()));
criteria.add(Restrictions.eq("locale", conceptStopWord.getLocale()));
List<ConceptStopWord> stopWordList = criteria.list();
if (!stopWordList.isEmpty()) {
throw new DAOException("Duplicate ConceptStopWord Entry");
}
sessionFactory.getCurrentSession().saveOrUpdate(conceptStopWord);
}
return conceptStopWord;
}
/**
* @see org.openmrs.api.db.ConceptDAO#deleteConceptStopWord(java.lang.Integer)
*/
public void deleteConceptStopWord(Integer conceptStopWordId) throws DAOException {
if (conceptStopWordId == null) {
throw new DAOException("conceptStopWordId is null");
}
Object csw = sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class).add(
Restrictions.eq("conceptStopWordId", conceptStopWordId)).uniqueResult();
if (csw == null) {
throw new DAOException("Concept Stop Word not found or already deleted");
}
sessionFactory.getCurrentSession().delete(csw);
}
/**
* @see org.openmrs.api.db.ConceptDAO#getAllConceptStopWords()
*/
public List<ConceptStopWord> getAllConceptStopWords() {
return sessionFactory.getCurrentSession().createCriteria(ConceptStopWord.class).list();
}
/**
* @see org.openmrs.api.db.ConceptDAO#getCountOfConceptWords(String, List, boolean, List, List,
* List, List, Concept)
*/
@Override
public Long getCountOfConceptWords(String phrase, List<Locale> locales, boolean includeRetired,
List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
List<ConceptDatatype> excludeDatatypes, Concept answersToConcept, boolean forUniqueConcepts) {
Criteria searchCriteria = createConceptWordSearchCriteria(phrase, locales, includeRetired, requireClasses,
excludeClasses, requireDatatypes, excludeDatatypes, answersToConcept);
if (searchCriteria != null) {
if (forUniqueConcepts)
searchCriteria.setProjection(Projections.countDistinct("concept"));
else
searchCriteria.setProjection(Projections.rowCount());
return (Long) searchCriteria.uniqueResult();
}
return (long) 0;
}
/**
* Utility method that returns a criteria for searching for conceptWords that match the
* specified search phrase and arguments
*
* @param phrase matched to the start of any word in any of the names of a concept
* @param locales List<Locale> to restrict to
* @param includeRetired boolean if false, will exclude retired concepts
* @param requireClasses List<ConceptClass> to restrict to
* @param excludeClasses List<ConceptClass> to leave out of results
* @param requireDatatypes List<ConceptDatatype> to restrict to
* @param excludeDatatypes List<ConceptDatatype> to leave out of results
* @param answersToConcept all results will be a possible answer to this concept
* @param start all results less than this number will be removed
* @param size if non zero, all results after <code>start</code> + <code>size</code> will be
* removed
* @return the generated criteria object
*/
private Criteria createConceptWordSearchCriteria(String phrase, List<Locale> locales, boolean includeRetired,
List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
List<ConceptDatatype> excludeDatatypes, Concept answersToConcept) throws DAOException {
//add the language-only portion of locale if its not in the list of locales already
List<Locale> localesToAdd = new Vector<Locale>();
for (Locale locale : locales) {
Locale languageOnly = new Locale(locale.getLanguage());
if (locales.contains(languageOnly) == false)
localesToAdd.add(languageOnly);
}
locales.addAll(localesToAdd);
//assumes getUniqueWords() removes quote(') characters. (otherwise we would have a security leak)
List<String> words = ConceptWord.getUniqueWords(phrase);
// these are the answers to restrict on
List<Concept> answers = new Vector<Concept>();
if (answersToConcept != null && answersToConcept.getAnswers(false) != null) {
for (ConceptAnswer conceptAnswer : answersToConcept.getAnswers(false)) {
answers.add(conceptAnswer.getAnswerConcept());
}
}
if (words.size() > 0 || !answers.isEmpty()) {
Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(ConceptWord.class, "cw1");
searchCriteria.add(Restrictions.in("locale", locales));
if (includeRetired == false) {
searchCriteria.createAlias("concept", "concept");
searchCriteria.add(Restrictions.eq("concept.retired", false));
}
// Only restrict on answers if there are any
if (!answers.isEmpty())
searchCriteria.add(Restrictions.in("cw1.concept", answers));
if (words.size() > 0) {
Iterator<String> word = words.iterator();
searchCriteria.add(Restrictions.like("word", word.next(), MatchMode.START));
Conjunction junction = Restrictions.conjunction();
while (word.hasNext()) {
String w = word.next();
if (log.isDebugEnabled())
log.debug("Current word: " + w);
DetachedCriteria crit = DetachedCriteria.forClass(ConceptWord.class).setProjection(
Property.forName("concept")).add(Restrictions.eqProperty("concept", "cw1.concept")).add(
Restrictions.like("word", w, MatchMode.START)).add(Restrictions.in("locale", locales));
junction.add(Subqueries.exists(crit));
}
searchCriteria.add(junction);
}
if (requireClasses.size() > 0)
searchCriteria.add(Restrictions.in("concept.conceptClass", requireClasses));
if (excludeClasses.size() > 0)
searchCriteria.add(Restrictions.not(Restrictions.in("concept.conceptClass", excludeClasses)));
if (requireDatatypes.size() > 0)
searchCriteria.add(Restrictions.in("concept.datatype", requireDatatypes));
if (excludeDatatypes.size() > 0)
searchCriteria.add(Restrictions.not(Restrictions.in("concept.datatype", excludeDatatypes)));
return searchCriteria;
}
return null;
}
/**
* @see ConceptService#getCountOfDrugs(String, Concept, boolean, boolean)
*/
public Long getCountOfDrugs(String drugName, Concept concept, boolean searchOnPhrase, boolean searchDrugConceptNames,
boolean includeRetired) throws DAOException {
Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
if (StringUtils.isBlank(drugName) && concept == null)
return (long) 0;
if (includeRetired == false)
searchCriteria.add(Restrictions.eq("drug.retired", false));
if (concept != null)
searchCriteria.add(Restrictions.eq("drug.concept", concept));
MatchMode matchMode = MatchMode.START;
if (searchOnPhrase)
matchMode = MatchMode.ANYWHERE;
if (!StringUtils.isBlank(drugName)) {
searchCriteria.add(Restrictions.ilike("drug.name", drugName, matchMode));
if (searchDrugConceptNames) {
searchCriteria.createCriteria("concept", "concept").createAlias("concept.names", "names");
searchCriteria.add(Restrictions.ilike("names.name", drugName, matchMode));
}
}
searchCriteria.setProjection(Projections.rowCount());
return (Long) searchCriteria.uniqueResult();
}
@SuppressWarnings("unchecked")
@Override
public List<Drug> getDrugs(String drugName, Concept concept, boolean searchOnPhrase, boolean searchDrugConceptNames,
boolean includeRetired, Integer start, Integer length) throws DAOException {
Criteria searchCriteria = sessionFactory.getCurrentSession().createCriteria(Drug.class, "drug");
if (StringUtils.isBlank(drugName) && concept == null)
return Collections.emptyList();
if (includeRetired == false)
searchCriteria.add(Restrictions.eq("drug.retired", false));
if (concept != null)
searchCriteria.add(Restrictions.eq("drug.concept", concept));
MatchMode matchMode = MatchMode.START;
if (searchOnPhrase)
matchMode = MatchMode.ANYWHERE;
if (!StringUtils.isBlank(drugName)) {
searchCriteria.add(Restrictions.ilike("drug.name", drugName, matchMode));
if (searchDrugConceptNames) {
searchCriteria.createCriteria("concept", "concept").createAlias("concept.names", "names");
searchCriteria.add(Restrictions.ilike("names.name", drugName, matchMode));
}
}
if (start != null)
searchCriteria.setFirstResult(start);
if (length != null && length > 0)
searchCriteria.setMaxResults(length);
return searchCriteria.list();
}
/**
* @see ConceptDAO#getConcepts(String, List, boolean, List, List, List, List, Concept, Integer,
* Integer)
*/
@SuppressWarnings( { "rawtypes" })
@Override
public List<ConceptSearchResult> getConcepts(String phrase, List<Locale> locales, boolean includeRetired,
List<ConceptClass> requireClasses, List<ConceptClass> excludeClasses, List<ConceptDatatype> requireDatatypes,
List<ConceptDatatype> excludeDatatypes, Concept answersToConcept, Integer start, Integer size)
throws DAOException {
Criteria searchCriteria = createConceptWordSearchCriteria(phrase, locales, includeRetired, requireClasses,
excludeClasses, requireDatatypes, excludeDatatypes, answersToConcept);
List<ConceptSearchResult> results = new Vector<ConceptSearchResult>();
if (searchCriteria != null) {
ProjectionList pl = Projections.projectionList();
pl.add(Projections.distinct(Projections.groupProperty("cw1.concept")));
pl.add(Projections.property("cw1.word"));
//if we have multiple words for the same concept, get the one with a highest weight
pl.add(Projections.max("cw1.weight"));
pl.add(Projections.property("cw1.conceptName"));
searchCriteria.setProjection(pl);
searchCriteria.addOrder(Order.desc("cw1.weight"));
if (start != null)
searchCriteria.setFirstResult(start);
if (size != null && size > 0)
searchCriteria.setMaxResults(size);
searchCriteria.setResultTransformer(Transformers.TO_LIST);
List resultObjects = searchCriteria.list();
for (Object obj : resultObjects) {
List list = (List) obj;
results.add(new ConceptSearchResult((String) list.get(1), (Concept) list.get(0), (ConceptName) list.get(3),
(Double) list.get(2)));
}
}
return results;
}
/**
* @see ConceptDAO#weighConceptWord(ConceptWord)
*/
@Override
public Double weighConceptWord(ConceptWord word) {
Double weight = 0.0;
String conceptName = word.getConceptName().getName().toUpperCase();
String wordString = word.getWord();
//why is this the case, this seems like invalid data
if (conceptName.indexOf(wordString) < 0)
return weight;
//by default every word must at least weigh 1+
weight = 1.0;
//TODO make the numbers 5.0, 3.0, 1.0 etc constants
//Index terms rank highly since they were added for searching
//This is the actual match
if (conceptName.equals(wordString)) {
double weightCoefficient = 5.0;
weight += weightCoefficient;
//the shorter the word, the higher the increment and the coefficient since it a closer
//match based on number of characters e.g 'OWN' should weigh more than 'HOME'
weightCoefficient += (weightCoefficient / wordString.length());
weight += (weightCoefficient / wordString.length());
//compute bonus based on the concept name type
weight += computeBonusWeight(weightCoefficient, word);
} else if (conceptName.startsWith(wordString)) {
double weightCoefficient = 3.0;
//the shorter the word, the higher the increment since it a closer match to the name
// e.g MY in 'MY DEPOT' should weigh more than HOME in 'HOME DEPOT'
weight += (weightCoefficient / wordString.length());
weight += computeBonusWeight(weightCoefficient, word);
} else {
double weightCoefficient = 1.0;
//still a shorter word should weigh more depending on its index in the full concept name
//e.g MY in 'IN MY HOME' should weigh more than 'MY' in 'FOR MY HOME', we add 1 so that
// if 'conceptName.indexOf(wordString)' returns 1, we still divide 5 by something greater than 1
//e.g 'MARRIAGE' in 'PRE MARRIAGE' should weigh more than 'MARRIAGE' in 'NOT PRE MARRIAGE'
//and still weigh more then 'MARRIAGE' in 'PRE MARRIAGE RELATIONSHIP'
weight += ((weightCoefficient / (conceptName.indexOf(wordString) + 1)) * ((conceptName.length() - wordString
.length()) / new Double(conceptName.length())));
weight += computeBonusWeight(weightCoefficient, word);
}
//round off to 3 decimal places
return Double.parseDouble(new DecimalFormat("0.000").format(weight));
}
/**
* Utility method that computes the bonus weight for a concept word based on the
* {@link ConceptNameType}, the length of the full concept name and the weightCoefficient
*
* @param weightCoefficient
* @param word
* @return
*/
private double computeBonusWeight(Double weightCoefficient, ConceptWord word) {
double bonusWeight = 0.0;
ConceptName conceptName = word.getConceptName();
if (conceptName.isIndexTerm()
|| (word.getConceptName().isPreferred() && word.getConceptName().isFullySpecifiedName()))
bonusWeight += weightCoefficient * 0.25;
else if (conceptName.isPreferred())
bonusWeight += weightCoefficient * 0.24;
else if (conceptName.isFullySpecifiedName())
bonusWeight += weightCoefficient * 0.23;
else if (conceptName.isSynonym())
bonusWeight += weightCoefficient * 0.22;
else if (conceptName.isShort())
bonusWeight += weightCoefficient * 0.21;
//the shorter the full concept name, the higher the weight, the word 'MEASELS' in
//'MEASELS ON EARTH' should weigh more than another 'MEASELS' in 'MEASELS ON JUPITER'
bonusWeight += weightCoefficient / new Double(word.getConceptName().getName().length());
return bonusWeight;
}
}